using System;
using System.Collections.Generic;
using System.Text;
using RapidHDL.Fabric;

namespace RapidHDL
{
    public class SNNCore : MicroCoreTable
    {
        public SNNCore()
            : base()
        {
            AddSimpleOps();
            AddBranchOps();
        }

        public void AddSimpleOp(string psName)
        {
            MicroCoreOp oOp = AddOp(psName);
            //string sControlState = psName.Replace("_C", "_CONST");
            oOp.AddStateInstruction(psName);
        }

        private void AddBranchOps()
        {

            MicroCoreOp oOp = AddOp("JMP_MEM_REQ");
            oOp.AddBranchInstruction("REQ", "REQ_READY");
            oOp.AddJumpInstruction("REQ_NOT_READY");
            oOp.AddStateInstruction("PC_JUMP", "REQ_READY");
            oOp.AddStateInstruction("NOP", "REQ_NOT_READY");

            oOp = AddOp("JMP_MEM_ERROR");
            oOp.AddBranchInstruction("MEM_ERROR", "MEM_ERROR_YES");
            oOp.AddJumpInstruction("MEM_ERROR_NO");
            oOp.AddStateInstruction("PC_JUMP", "MEM_ERROR_YES");
            oOp.AddStateInstruction("NOP", "MEM_ERROR_NO");


            oOp = AddOp("JMP_EQI");
            oOp.AddBranchInstruction("EQI_READY", "EQI_READY");
            oOp.AddJumpInstruction("EQI_NOT_READY");
            oOp.AddStateInstruction("PC_JUMP","EQI_READY");
            oOp.AddStateInstruction("NOP", "EQI_NOT_READY");

            oOp = AddOp("JMP_EQO");
            oOp.AddBranchInstruction("EQO_STALL", "EQO_NOT_READY");
            oOp.AddJumpInstruction("EQO_READY");
            oOp.AddStateInstruction("PC_JUMP", "EQO_NOT_READY");
            oOp.AddStateInstruction("NOP", "EQO_READY");


            oOp = AddOp("JMP_MEMQI");
            oOp.AddBranchInstruction("MEMQI_READY", "MEMQI_READY");
            oOp.AddJumpInstruction("MEMQI_NOT_READY");
            oOp.AddStateInstruction("PC_JUMP", "MEMQI_READY");
            oOp.AddStateInstruction("NOP", "MEMQI_NOT_READY");

            oOp = AddOp("BEQ");
            oOp.AddBranchInstruction("EQ", "JMP_0_EQ");
            oOp.AddJumpInstruction("JMP_0_DONE");
            oOp.AddStateInstruction("PC_JUMP", "JMP_0_EQ");
            oOp.AddStateInstruction("NOP", "JMP_0_DONE");

            oOp = AddOp("BGE");
            oOp.AddBranchInstruction("GT", "JMP_GT");
            oOp.AddJumpInstruction("JMP_GT_DONE");
            oOp.AddStateInstruction("PC_JUMP", "JMP_GT");
            oOp.AddStateInstruction("NOP", "JMP_GT_DONE");


            oOp = AddOp("JMP");
            oOp.AddStateInstruction("PC_JUMP");
            oOp.AddStateInstruction("NOP");
            oOp.AddStateInstruction("NOP");
            oOp.AddStateInstruction("NOP");

            oOp = AddOp("JSR");
            oOp.AddStateInstruction("PUSH_PC");
            oOp.AddStateInstruction("SP_INC");
            oOp.AddStateInstruction("PC_JUMP");
            oOp.AddStateInstruction("NOP");
            oOp.AddStateInstruction("NOP");

            oOp = AddOp("RTS");
            oOp.AddStateInstruction("SP_DEC");
            oOp.AddStateInstruction("NOP");
            oOp.AddStateInstruction("NOP");
            oOp.AddStateInstruction("PULL_PC");
            oOp.AddStateInstruction("NOP");
            oOp.AddStateInstruction("NOP");

            MemoryInstruction("A", true);
            MemoryInstruction("B", true);
            MemoryInstruction("A", false );
            MemoryInstruction("B", false );

            //Add a time-out register here
            /*oOp = AddOp("READ_A_C");
            oOp.AddBranchInstruction("MEMQI_READY","READ_A_C_START","READ_A_C_CHECK_EMPTY");
            oOp.AddJumpInstruction("READ_A_C_STALL");
            oOp.AddStateInstruction("MEM_CLEAR");
            oOp.AddJumpInstruction("READ_A_C_CHECK_EMPTY");
            oOp.AddBranchInstruction("MEMQO_STALL", "READ_A_C_STALL", "READ_A_C_STALL");
            oOp.AddStateInstruction("MEM_READ_POST_A","READ_A_C_START");
            oOp.AddBranchInstruction("MEMQI_READY", "READ_A_C_DATA_IN", "WAIT_READ_A_C_DATA_IN");
            oOp.AddJumpInstruction("WAIT_READ_A_C_DATA_IN");
            oOp.AddBranchInstruction("REQ", "READ_A_C_DONE", "READ_A_C_DATA_IN");
            oOp.AddJumpInstruction("WAIT_READ_A_C_DATA_IN");
            oOp.AddStateInstruction("MEM_READ","READ_A_C_DONE");
             */
        }



        private void MemoryInstruction(string psReg, bool pbRead)
        {
            string sOP = "";
            string sPostIntruction = "";
            if (pbRead)
            {
                sOP = "READ_" + psReg;
                sPostIntruction = "MEM_POST_READ_" + psReg;
            }
            else
            {
                sOP = "WRITE_" + psReg;
                sPostIntruction = "MEM_POST_WRITE_" + psReg;
            }

            MicroCoreOp oOp = AddOp(sOP);
            oOp.AddStateInstruction("MEM_RESET");
            oOp.AddBranchInstruction("MEMQI_READY", sOP + "CLEAR_DATA", sOP + "CLEAR");  // Clear the Q First
            oOp.AddStateInstruction(sPostIntruction, sOP + "POST");  // post the request
            for (int iDelay = 0; iDelay < 10; iDelay++)
                oOp.AddStateInstruction("NOP");
            oOp.AddStateInstruction("MC_INC",sOP + "TEST_READY");
            oOp.AddBranchInstruction("MEM_TIMEOUT", sOP + "ERROR");
            //oOp.AddBranchInstruction("MEM_RETRY", sOP + "RETRY");
            oOp.AddBranchInstruction("MEMQI_READY", sOP + "READY"); // "Was ReADY" // check to see if request is ready
            oOp.AddJumpInstruction(sOP + "TEST_READY");

            //Retry - diabled for now
            //oOp.AddStateInstruction(sPostIntruction, sOP + "RETRY");  // post the request
            //oOp.AddStateInstruction("MC_INC",sOP + "TEST_READY_RETRY");
            //oOp.AddBranchInstruction("MEM_TIMEOUT", sOP + "ERROR");
            //oOp.AddBranchInstruction("MEMQI_READY", sOP + "READY"); // check to see if request is ready
            //oOp.AddJumpInstruction(sOP + "TEST_READY_RETRY");

            
            //oOp.AddBranchInstruction("REQ", sOP + "GOOD", sOP + "READY"); // data is ready
            //oOp.AddStateInstruction("MEM_CLEAR");
            //oOp.AddBranchInstruction("MEM_RETRY", sOP + "TEST_READY_RETRY");
            //oOp.AddJumpInstruction(sOP + "TEST_READY");

            //Q Clearing Routine
            oOp.AddStateInstruction("MEM_CLEAR", sOP + "CLEAR_DATA");
            oOp.AddJumpInstruction(sOP + "CLEAR");

            //Finish
            oOp.AddStateInstruction("MEM_ERROR", sOP + "ERROR");
            //oOp.AddJumpInstruction(sOP + "DONE");
            oOp.AddStateInstruction("NOP", sOP + "READY");  // bypass the REQ stuff above
            if (pbRead)
            {
                oOp.AddStateInstruction("LOAD_DR_MEMQI", sOP + "GOOD");
                oOp.AddStateInstruction("MEM_CLEAR");
            }
            else
                oOp.AddStateInstruction("MEM_CLEAR", sOP + "GOOD");

        }





        private void AddPushPullOp(string psRegister)
        {
            MicroCoreOp oOp = AddOp("PUSH_" + psRegister);
            oOp.AddStateInstruction("PUSH_" + psRegister);
            oOp.AddStateInstruction("SP_INC");

            oOp = AddOp("PULL_" + psRegister);
            oOp.AddStateInstruction("SP_DEC");
            oOp.AddStateInstruction("NOP");
            oOp.AddStateInstruction("NOP");
            oOp.AddStateInstruction("PULL_" + psRegister);
        }

        private void AddSimpleOps()
        {
            AddSimpleOp("NOP");

            AddSimpleOp("SP_INC");
            AddSimpleOp("SP_DEC");

            AddSimpleOp("EQI_POST_WRITE");
            AddSimpleOp("EQI_POST_READ");

            AddSimpleOp("COMP_QT_C");
            AddSimpleOp("LOAD_A_B");
            AddSimpleOp("LOAD_B_A");
            AddSimpleOp("LOAD_QO_A");
            AddSimpleOp("LOAD_A_QI");
            AddSimpleOp("LOAD_QO_B");
            AddSimpleOp("LOAD_B_QI");
            AddSimpleOp("LOAD_DR_A");
            AddSimpleOp("LOAD_A_DR");
            AddSimpleOp("LOAD_DR_B");
            AddSimpleOp("LOAD_B_DR");
            AddSimpleOp("LOAD_A_C");
            AddSimpleOp("LOAD_B_C");
            AddSimpleOp("LOAD_DR_C");
            AddSimpleOp("LOAD_QO_QI");

            AddSimpleOp("COMP_A_C");
            AddSimpleOp("COMP_B_C");
            AddSimpleOp("COMP_DR_C");

            AddPushPullOp("A");
            AddPushPullOp("B");
            AddPushPullOp("DR");
            AddPushPullOp("PC");
            AddPushPullOp("SR");
            AddPushPullOp("QI");
            AddPushPullOp("QT");
            AddPushPullOp("QO");


            AddSimpleOp("AND_A_C");
            AddSimpleOp("AND_B_C");
            AddSimpleOp("OR_A_C");
            AddSimpleOp("OR_B_C");
            AddSimpleOp("ADD_A_C");
            AddSimpleOp("ADD_B_C");
            AddSimpleOp("SUB_A_C");
            //AddSimpleOp("SUB_B_C");
            AddSimpleOp("AND_A_B");
            AddSimpleOp("OR_A_B");
            AddSimpleOp("ADD_A_B");
            AddSimpleOp("SUB_A_B");

            AddSimpleOp("LOAD_A_QA");
            //AddSimpleOp("LOAD_B_QA");

            AddSimpleOp("LOAD_QT_A");
            AddSimpleOp("LOAD_QT_B");

            AddSimpleOp("EQI_READ");
            AddSimpleOp("MEM_POST_READ_A");
            AddSimpleOp("MEM_POST_READ_B");
            AddSimpleOp("MEM_POST_WRITE_A");
            AddSimpleOp("MEM_POST_WRITE_B");
            AddSimpleOp("MEM_CLEAR");
            AddSimpleOp("LOAD_DR_MEMQI");
            AddSimpleOp("MEM_RESET");
            AddSimpleOp("MC_INC");
            AddSimpleOp("COMP_MC_C");
            //AddSimpleOp("WRITE_PROG_RAM");

            AddSimpleOp("LSR8_B");
            AddSimpleOp("LSL8_B");
            AddSimpleOp("LSL14_B");
            //AddSimpleOp("LSR14_B");
            //AddSimpleOp("LSL17_A");
            AddSimpleOp("LSR17_A");

            AddSimpleOp("PUSH_NA");  //new
            AddSimpleOp("PUSH_NC");
            AddSimpleOp("PUSH_NL");
            AddSimpleOp("PUSH_NT");
            AddSimpleOp("PULL_NA");
            AddSimpleOp("PULL_NC");
            AddSimpleOp("PULL_NL");
            AddSimpleOp("PULL_NT");
            AddSimpleOp("LSR20_A");
            //AddSimpleOp("LSL20_A");

            AddSimpleOp("COMP_A_B");
            AddSimpleOp("WRITE_NEURON");
            AddSimpleOp("READ_NEURON");


        }
    }
}
